﻿using NanoMvvm.Collections;
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace NanoMvvm
{
    public enum ShowType
    {
        Normal,
        Modal
    }

    public static class ViewManager
    {
        #region ShowType ADP

        /// <summary>
        /// ShowType Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty ShowTypeProperty =
            DependencyProperty.RegisterAttached("ShowType", typeof(ShowType), typeof(ViewManager),
                                                new PropertyMetadata(ShowType.Normal));

        /// <summary>
        /// TopMost Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty TopMostProperty =
            DependencyProperty.RegisterAttached("TopMost", typeof(bool), typeof(ViewManager),
                                                new PropertyMetadata(false));

        private static readonly WeakKeyedDictionary<Window, SynchronizationContext> ContextRegistry
                    = new WeakKeyedDictionary<Window, SynchronizationContext>();

        private static readonly WeakKeyedDictionary<object, Window> VmRegistry
                    = new WeakKeyedDictionary<object, Window>();

        private static volatile bool fromClosingEvent;

        internal static int ViewCount
        {
            get
            {
                return VmRegistry.Count;
            }
        }

        /// <summary>
        /// Gets the ShowType property.
        /// </summary>
        [AttachedPropertyBrowsableForType(typeof(Window))]
        public static ShowType GetShowType(Window w)
        {
            return (ShowType)w.GetValue(ShowTypeProperty);
        }

        /// <summary>
        /// Sets the ShowType property.
        /// </summary>
        public static void SetShowType(Window w, ShowType value)
        {
            w.SetValue(ShowTypeProperty, value);
        }

        #endregion ShowType ADP

        #region TopMost ADP

        /// <summary>
        /// Gets the TopMost property.
        /// </summary>
        [AttachedPropertyBrowsableForType(typeof(Window))]
        public static bool GetTopMost(Window w)
        {
            return (bool)w.GetValue(TopMostProperty);
        }

        /// <summary>
        /// Sets the TopMost property.
        /// </summary>
        public static void SetTopMost(Window w, bool value)
        {
            w.SetValue(TopMostProperty, value);
        }

        #endregion TopMost ADP

        internal static void SetupView(WorkspaceViewModel viewModel, Type viewType)
        {
            if (!typeof(Window).IsAssignableFrom(viewType))
            {
                throw new ArgumentException("The workspace view type must inherit from System.Windows.Window.");
            }

            var context = NanoMvvmFramework.Instance.Synchronization.Main;

            if (context != null)
            {
                context.Send(state => SetupViewImpl(viewModel, viewType, context), context);
            }
            else
            {
                throw new InvalidOperationException("Sync Context is null.");
            }
        }

        internal static bool TearDownView(WorkspaceViewModel viewModel)
        {
            if (VmRegistry.ContainsKey(viewModel))
            {
                if (!viewModel.PreProcessDeactivation())
                {
                    return false;
                }

                viewModel.SetDeactivatedInternal();

                var w = VmRegistry[viewModel];

                if (ContextRegistry.ContainsKey(w))
                {
                    var context = ContextRegistry[w];

                    if (context != null)
                    {
                        context.Send(state => TeardownViewImpl(w), null);

                        ContextRegistry.Remove(w);
                        VmRegistry.Remove(viewModel);

                        if (!(viewModel is SplashViewModel) && ViewCount == 0)
                        {
                            NanoMvvmFramework.Instance.Application.Shutdown();
                        }

                        return true;
                    }

                    throw new InvalidOperationException("Sync Context is null.");
                }
            }

            return false;
        }

        private static void SetupViewImpl(WorkspaceViewModel vm, Type winType, SynchronizationContext context)
        {
            if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
            {
                throw new InvalidOperationException("Cannot create Workspace Views on a non-STA thread.");
            }

            var w = (Window)Activator.CreateInstance(winType);

            w.Closing += WClosing;

            w.DataContext = vm;

            var showType = GetShowType(w);

            ContextRegistry.Add(w, context);
            VmRegistry.Add(vm, w);

            if (showType == ShowType.Normal)
            {
                w.Show();
            }
            else if (showType == ShowType.Modal)
            {
                w.ShowDialog();
            }
        }

        private static void WClosing(object sender, CancelEventArgs e)
        {
            var w = sender as Window;

            var wvm = w.DataContext as WorkspaceViewModel;

            if (wvm == null)
            {
                return;
            }

            fromClosingEvent = true;

            if (!TearDownView(wvm))
            {
                e.Cancel = true;
            }

            fromClosingEvent = false;
        }

        private static void TeardownViewImpl(Window view)
        {
            view.Closing -= WClosing;

            view.DataContext = null;

            if (!fromClosingEvent)
            {
                view.Close();
            }
        }
    }
}